import { dependence, DepConfig, FoundDependence } from "../typings/dep";

/**
 * @author tsdy
 * @description data上数据与computed挂在data上数据间建立依赖关系
 */
export default class Dep {
  /**
   * 对于组件内部的响应式我们通过，在组件内部实例化一个Dep。
   * 通过监听者模式，获取计算属性（computed）的计算时需要用到的data上的属性（data.attribute)
   * （我们称之为data.attribute->computed，computed依赖于data.attribute）
   * 将这些依赖关系存入Dependence，当data.attribute出现变化时，组件proxy中的set函数会调用findDep去查找依赖于data.attribute的计算属性。
   */
  private Dependence: Record<string, Array<dependence>> = {};
  private DepHash: Record<string, boolean>;
  constructor(config: DepConfig) {
    this.DepHash = config.DepHash;
  }
  /**
   * @example
   * 我们想要知道计算属性依赖于哪些proxy（代理的data）上的属性。
   * 就要将get函数放在该proxy的get拦截函数中。
   * @param path
   */
  public get(path: string) {
    if (this.DepHash[path] == undefined) {
      this.DepHash[path] = false;
    }

    this.DepHash[path] = true;
  }
  /**
   *
   * @param key 在这里约定key->value ,为依赖关系
   * @param id 数据中心与组件建立依赖关系时，需要传递组件唯一id
   */
  public findDepValue(key: string, id?: number): Array<FoundDependence> {
    let foundDepList: Array<FoundDependence> = [];
    Object.keys(this.Dependence).forEach((depName) => {
      Object.values(this.Dependence[depName]).forEach((dep) => {
        let foundDep: FoundDependence | undefined;
        if (Number.isInteger(Number(dep.id))) {
          if (dep.key == key) {
            foundDep = {
              ...dep,
              depName,
            };
          }
        } else {
          if (dep.key == key) {
            foundDep = {
              ...dep,
              depName,
            };
          }
        }
        if (foundDep) {
          foundDepList.push(foundDep);
        }
      });
    });
    return foundDepList;
  }
  /**
   * 设置依赖关系
   * 先将DepHash属性置false
   * 调用回调函数（计算，计算属性）
   * 通过遍历DepHash，属性为true，就为该计算属性的依赖属性
   * @param value key->value为依赖关系
   * @param depName Dependence中的键名
   * @param calback
   * @param id 只有数据中心与组件建立依赖关系时，才会传递组件唯一id
   */
  public setDependence(
    value: string,
    depName: string,
    calback: Function,
    id?: number
  ) {
    this.setDepHashFalse();
    calback();
    this.appendDependence(value, depName, id);
  }
  /**
   *
   * @param key
   * @param value
   * 在以知对应关系时，直接设置依赖关系
   */
  // public setDepKeyValue(key: string, value: string) {
  //     this.Dependence.push({
  //         key, value
  //     })
  // }
  /**
   *
   * @param value key->value为依赖关系
   * @param depName Dependence中的键名
   * @param id
   */
  private appendDependence(value: string, depName: string, id?: number) {
    if (!this.Dependence[depName]) {
      this.Dependence[depName] = [];
    }
    Object.keys(this.DepHash).forEach((key) => {
      if (this.DepHash[key] == true) {
        if (Number.isInteger(<number>id)) {
          this.Dependence[depName].push({
            id,
            key,
            value,
          });
        } else {
          this.Dependence[depName].push({
            key,
            value,
          });
        }
      }
    });
  }
  private setDepHashFalse(): void {
    Object.keys(this.DepHash).forEach((key) => {
      this.DepHash[key] = false;
    });
  }
}
